package com.visualexpress.dao;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Example;
import org.hibernate.shards.strategy.ShardStrategyFactory;

import com.visualexpress.data.Order;
import com.visualexpress.data.Time;
import com.visualexpress.domain.Location;
import com.visualexpress.domain.LocationPairShipment;
import com.visualexpress.domain.Shipment;
import com.visualexpress.domain.TimeLocation;
import com.visualexpress.shards.HibernateSessionFactory;
import com.visualexpress.shards.ShipmentShardStrategyFactory;
import com.visualexpress.shards.VEShardStrategyFactory;
import com.visualexpress.util.DateUtil;

/**
 * A data access object (DAO) providing persistence and search support for
 * Shipment entities. Transaction control of the save(), update() and delete()
 * operations can directly support Spring container-managed transactions or they
 * can be augmented to handle user-managed Spring transactions. Each of these
 * methods provides additional information for how to configure it for the
 * desired type of transaction control.
 * 
 * @see com.visualexpress.domain.Shipment
 * @author MyEclipse Persistence Tools
 */

public class ShipmentDAO implements IBaseHibernateDAO {
	private static final Log log = LogFactory.getLog(ShipmentDAO.class);

	// property constants

	public static final String SHIPMENT_NUM = "shipmentNum";
	public static final String START_LOCATION = "startLocation";
	public static final String END_LOCATION = "endLocation";
	public static final String USETIME = "usetime";
	public static final String STATE = "state";
	public static final String IS_COUNTED = "isCounted";
	private static ShardStrategyFactory ssf ;
	public void save(Shipment transientInstance) {
		log.debug("saving Shipment instance");
		try {
			Session s = this.getSession();
			Transaction t = s.beginTransaction();
			s.save(transientInstance);
//			s.flush();
			t.commit();
//			s.close();
			System.out.println(transientInstance.getId());
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
	}
	
	public void saveOrUpdate(Shipment persistentInstance) {
		log.debug("saving Shipment instance");
		try {
			Session s = this.getSession();
			Transaction t = s.beginTransaction();
			s.saveOrUpdate(persistentInstance);
//			s.flush();
			t.commit();
//			s.close();
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
	}
	
	public void saveOrder(Order o){
		ShipmentDAO sdao = new ShipmentDAO();
		LocationDAO ldao = new LocationDAO();
		Shipment news = null;
		Location startLocation = null; 
		Location endLocation = null;
		Long usetime = null;
		List listEnd = null;
		List listStart = ldao.findByName(o.getStartCity());
		if(o.isDeliverid()){
			listEnd = ldao.findByName(o.getEndCity());
			if(!listStart.isEmpty()){
				startLocation = (Location)listStart.get(0);
			}
			if(!listEnd.isEmpty()){
				endLocation = (Location)listEnd.get(0);
			}
			if(o.getEndTime()!= null){
				 usetime = (DateUtil.transformToDate(o.getEndTime()).getTime()-DateUtil.transformToDate(o.getStartTime()).getTime())/(60*60*1000);
				 
			}
			news = new Shipment();
			news.setEndLocation(endLocation);
			news.setIsCounted(false);
			news.setStartLocation(startLocation);
			news.setShipmentNum(o.getOrderNumber());
			news.setUsetime(usetime);
			news.setState(true);
			sdao.save(news);
		}
		else{
			if(!listStart.isEmpty()){
				startLocation = (Location)listStart.get(0);
			}
			news = new Shipment();
			news.setStartLocation(startLocation);
			news.setIsCounted(false);
			news.setState(false);
			sdao.save(news);
		}
		
		TimeLocationDAO tdao = new TimeLocationDAO();
		ArrayList<String> passByCities = o.getPassByCities();
		ArrayList<Time> arriveTimes = o.getArriveTimes();
		ArrayList<Time> leaveTimes = o.getLeaveTimes();
		for(int j=0;j<passByCities.size();j++){
			String city = passByCities.get(j);
			Time arriveTime = arriveTimes.get(j);
			Time leaveTime = leaveTimes.get(j);
			Location loc = (Location)ldao.findByName(city).get(0);
			Date arrivalDate = DateUtil.transformToDate(arriveTime);
			Date leaveDate = DateUtil.transformToDate(leaveTime);
			TimeLocation tl = new TimeLocation();
			tl.setArrivalTime(arrivalDate);
			tl.setLeaveTime(leaveDate);
			tl.setLocation(loc);
			tl.setShipment(news);
			tl.setLocationNumber(j+1);
			tdao.save(tl);
		}
		
		LocationPairShipmentDAO lpsDAO = new LocationPairShipmentDAO(); 
		for(int j = 0; j < passByCities.size() - 1; j++) {
			for(int k = j + 1; k < passByCities.size(); k++) {
				String city = passByCities.get(j);
				Location loc1 = (Location)ldao.findByName(city).get(0);
				city = passByCities.get(k);
				Location loc2 = (Location)ldao.findByName(city).get(0);
				LocationPairShipment locPair = new LocationPairShipment();
				locPair.setLoc1(loc1); 
				locPair.setLoc2(loc2);
				locPair.setShipment(news);
				lpsDAO.save(locPair);
			}
		}
	}
	
	public void updateOrder(Order o){
		
	}
	
	public void saveWithId(Shipment transientInstance) {
		log.debug("saving Shipment instance");
		try {
			Session s = this.getSession();
			Transaction t = s.beginTransaction();
			s.save(transientInstance);
			t.commit();
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
	}

	public void delete(Shipment persistentInstance) {
		log.debug("deleting Shipment instance");
		try {
			Session s = this.getSession();
			Transaction t = s.beginTransaction();
			s.delete(persistentInstance);
			t.commit();
			log.debug("delete successful");
		} catch (RuntimeException re) {
			log.error("delete failed", re);
			throw re;
		}
	}

	public Shipment findById(String id) {
		log.debug("getting Shipment instance with id: " + id);
		try {
			Shipment instance = (Shipment) getSession().get(
					"com.visualexpress.domain.Shipment", id);
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}

	public List findByExample(Shipment instance) {
		log.debug("finding Shipment instance by example");
		try {
			List results = getSession().createCriteria(
					"com.visualexpress.domain.Shipment").add(
					Example.create(instance)).list();
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (RuntimeException re) {
			log.error("find by example failed", re);
			throw re;
		}
	}

	public List findByProperty(String propertyName, Object value) {
		log.debug("finding Shipment instance with property: " + propertyName
				+ ", value: " + value);
		try {
			getSession().flush();
			String queryString = "from Shipment as model where model."
					+ propertyName + "= ?";
			Query queryObject = getSession().createQuery(queryString);
			queryObject.setParameter(0, value);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	
	public List findByShipmentNum(Object shipmentNum) {
		return findByProperty(SHIPMENT_NUM, shipmentNum);
	}
	public List findByStartLocation(Object startLocation) {
		return findByProperty(START_LOCATION, startLocation);
	}

	public List findByEndLocation(Object endLocation) {
		return findByProperty(END_LOCATION, endLocation);
	}

	public List findByUsetime(Object usetime) {
		return findByProperty(USETIME, usetime);
	}

	public List findByIsCounted(Object isCounted) {
		return findByProperty(IS_COUNTED, isCounted);
	}
	
	public List findByState(Object state) {
		return findByProperty(STATE, state);
	}
	

	public List findAll() {
		log.debug("finding all Shipment instances");
		try {
			String queryString = "from Shipment";
			Query queryObject = getSession().createQuery(queryString);
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}

	public Shipment merge(Shipment detachedInstance) {
		log.debug("merging Shipment instance");
		try {
			Shipment result = (Shipment) getSession().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	public void attachDirty(Shipment instance) {
		log.debug("attaching dirty Shipment instance");
		try {
			Session s = this.getSession();
			Transaction t = s.beginTransaction();
			s.saveOrUpdate(instance);
			t.commit();
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void attachClean(Shipment instance) {
		log.debug("attaching clean Shipment instance");
		try {
			getSession().lock(instance, LockMode.NONE);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	
	public Session getSession() {
		
		if(ssf==null)
			ssf = new ShipmentShardStrategyFactory();
		return HibernateSessionFactory.getSession(ssf);
	}
}